﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.DirectX.DirectSound;
using System.Threading;
using System.IO;

namespace SoundServant.Controls
{
    /// <summary>
    /// Interaction logic for VolumeMeter.xaml
    /// </summary>
    partial class VolumeMeterControl : UserControl
    {
        public VolumeMeterControl()
        {
            InitializeComponent();
        }
        private delegate void VoidInvokeHandler();

        const short channels = 1;
        const short bitsPerSample = 16;
        const int samplesPerSecond = 44100;
        const int bytesPerSecond = samplesPerSecond * channels * bitsPerSample / 8;
        const int sampleFrequency = 25; // Times Per Second
        const int drawFrequency = 1000 / 100; //Times Per Second
        const double upIncrements = 0.5;
        const double downIncrements = 0.1;

        Thread captureThread;
        ThreadStart captureThreadStart;
        CaptureBufferDescription captureBufferDescription;
        CaptureBuffer captureBuffer;
        WaveFormat captureWaveFormat;
        Capture captureDevice;
        AutoResetEvent captureResetEvent;
        Notify captureNotify;

        double meterWidth = 20;
        double actualVolumeLevel = 1;
        double scaledPeakVolumeLevel = 1;
        double scaledVolumeLevel = 1;

        public bool IsRunning
        {
            get
            {
                if (captureThread != null && captureThread.IsAlive)
                    return true;
                else
                    return false;
            }
        }

        long peakPersistTime = 2 * TimeSpan.TicksPerSecond;
        long peakTime;

        bool _stop = false;

        private void DrawVolumeMeter()
        {
            this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Send, new VoidInvokeHandler(DrawVolumeMeterInvoked));
        }

        private void DrawVolumeMeterInvoked()
        {
            VolumeLevelBorder.Width = (meterWidth / 54D) * (scaledVolumeLevel);
            PeakVolumeLevelBorder.Margin = new Thickness((meterWidth / 54D) * (scaledPeakVolumeLevel), 0, -6, 0);
            if (scaledPeakVolumeLevel >= 54)
                OverBorder.Visibility = Visibility.Visible;
            else
                OverBorder.Visibility = Visibility.Hidden;
        }

        public void Start()
        {
            captureWaveFormat = new WaveFormat();
            captureWaveFormat.Channels = channels;
            captureWaveFormat.FormatTag = WaveFormatTag.Pcm;
            captureWaveFormat.SamplesPerSecond = samplesPerSecond;
            captureWaveFormat.BitsPerSample = bitsPerSample;
            captureWaveFormat.BlockAlign = (short)(channels * (bitsPerSample / (short)8));
            captureWaveFormat.AverageBytesPerSecond = captureWaveFormat.BlockAlign * samplesPerSecond;

            CaptureDevicesCollection capDevices = new CaptureDevicesCollection();
            Capture capDevice = new Capture(capDevices[0].DriverGuid);
            captureBufferDescription = new CaptureBufferDescription();
            captureBufferDescription.BufferBytes = bytesPerSecond / sampleFrequency * 2;
            captureBufferDescription.Format = captureWaveFormat;
            captureBuffer = new CaptureBuffer(captureBufferDescription, capDevice);

            captureResetEvent = new AutoResetEvent(false);
            captureNotify = new Notify(captureBuffer);
            BufferPositionNotify bufferPositionNotify1 = new BufferPositionNotify();
            bufferPositionNotify1.Offset = (bytesPerSecond / sampleFrequency) - 1;
            bufferPositionNotify1.EventNotifyHandle = captureResetEvent.Handle;
            BufferPositionNotify bufferPositionNotify2 = new BufferPositionNotify();
            bufferPositionNotify2.Offset = (bytesPerSecond / sampleFrequency * 2) - 1;
            bufferPositionNotify2.EventNotifyHandle = captureResetEvent.Handle;
            captureNotify.SetNotificationPositions(new BufferPositionNotify[] { bufferPositionNotify1, bufferPositionNotify2 });


            captureThreadStart = new ThreadStart(MeterThread);
            captureThread = new Thread(captureThreadStart);
            captureThread.Priority = ThreadPriority.BelowNormal;

            PeakVolumeLevelBorder.Visibility = Visibility.Visible;
            VolumeLevelBorder.Visibility = Visibility.Visible;

            captureThread.Start();
        }


        void MeterThread()
        {
            int bufferBite = bytesPerSecond / sampleFrequency;
            captureBuffer.Start(true); ushort bufferPosition = 1; int bufferOffset = 0;
            while (true)
            {

                captureResetEvent.WaitOne();
                Array samples = captureBuffer.Read(bufferOffset, typeof(Int16), LockFlag.None, new int[] { (bytesPerSecond / sampleFrequency) / 2 });
                int peak = 0;
                int workingValue = 0;
                foreach (Int16 sample in samples)
                {

                    if (sample < 0)
                        workingValue = sample * -1;
                    else
                        workingValue = sample;
                    if (sample > peak) peak = workingValue;
                }

                double volumeLevelGoal = 20 * Math.Log10((double)peak / Int16.MaxValue);

                if (volumeLevelGoal < -48 && volumeLevelGoal > -96)
                {
                    volumeLevelGoal = -48 + ((volumeLevelGoal + 48) / 8);
                }
                else if (volumeLevelGoal < -96 || double.IsInfinity(volumeLevelGoal) || double.IsNaN(volumeLevelGoal))
                {
                    volumeLevelGoal = -54;
                }

                volumeLevelGoal += 54D;

                if ((DateTime.Now.Ticks - peakTime >= peakPersistTime) || scaledPeakVolumeLevel <= scaledVolumeLevel)
                {
                    peakTime = DateTime.Now.Ticks;
                    scaledPeakVolumeLevel = scaledVolumeLevel;
                }

                /*
                int capturePosition = 0;
                int readPosition = 0;
                while (true)
                {
                    captureBuffer.GetCurrentPosition(out capturePosition, out readPosition);
                    if ((bufferPosition == 1 && capturePosition > bufferBite) || (bufferPosition == 2 && capturePosition < bufferBite))
                        break;

                    if (volumeLevelGoal > scaledVolumeLevel)
                    {
                        if (scaledVolumeLevel + upIncrements < volumeLevelGoal)
                            scaledVolumeLevel += upIncrements;
                        else
                            scaledVolumeLevel = volumeLevelGoal;
                    }
                    else if (volumeLevelGoal < scaledVolumeLevel && scaledVolumeLevel - downIncrements > volumeLevelGoal)
                    {
                        if (scaledVolumeLevel - upIncrements > volumeLevelGoal)
                            scaledVolumeLevel -= downIncrements;
                        else
                            scaledVolumeLevel = volumeLevelGoal;
                    }
                    Thread.Sleep(drawFrequency);
                    DrawVolumeMeter();
                }
                */

                scaledVolumeLevel = volumeLevelGoal;
                DrawVolumeMeter();

                if (_stop) { _stop = false; captureBuffer.Stop(); break; }

                if (bufferPosition == 1)
                { bufferOffset = bufferBite; bufferPosition = 2; }
                else
                { bufferOffset = 0; bufferPosition = 1; }
            }
        }

        private void VolumeMeterGrid_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (!double.IsNaN(e.NewSize.Width))
                meterWidth = e.NewSize.Width;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {

        }

        public void Stop()
        {
            PeakVolumeLevelBorder.Visibility = Visibility.Hidden;
            VolumeLevelBorder.Visibility = Visibility.Hidden;
            OverBorder.Visibility = Visibility.Hidden;
            _stop = true;
        }
    }
}